using UnityEngine;
using static PlayerStateEnum; //simplify the use of Enum code

/// <summary>
/// Offer Service To Other Modules, Jie Ou.
/// </summary>

public class PlayerController :
	MonoBehaviour, IAnimeMgr<PlayerStateEnum>, IInputMgr, IRigidBodyMgr {

	Animator _animator;
	Rigidbody2D _rigidbody;
	Transform _transform;

	IAnimeMgr<PlayerStateEnum> _animeMgr;
	IInputMgr _inputMgr;
	IRigidBodyMgr _rigidBodyMgr;
	StateFactory<PlayerStateEnum> _stateFactory;
	State<PlayerStateEnum> _rootFSM;

	BaseRayDetect _groundDetect;
	BaseRayDetect _ceilingDetect;
	BaseRayDetect _wallDetect;
	BaseRayDetect _edgeDetect;

	PlayerConfigData ConfigData;

	void Awake() {
		_inputMgr = new PlayerInputMgr();
		_animator = GetComponent<Animator>();
		_animeMgr = new AnimeMgr<PlayerStateEnum>(_animator);
		_rigidbody = GetComponent<Rigidbody2D>();
		_rigidBodyMgr = new RigidBodyMgr(_rigidbody, ConfigData.Gravity);
		_stateFactory = new StateFactory<PlayerStateEnum>();
		_rootFSM = new PlayerRootHFSM(this, PlayerStateEnum.RootHFSM, null, _stateFactory);
		_transform = gameObject.transform;

		AddStateToFactory();
		InitAnimeMgr();
		InitDetects();
	}

	void Update() {
	}

	void InitAnimeMgr() {
		AddAnim(PlayerStateEnum.Idle, "PlayerIdle");
		AddAnim(PlayerStateEnum.Run, "PlayerRun");
		AddAnim(PlayerStateEnum.Jump, "PlayerJump");
	}

	void AddStateToFactory() {
		_stateFactory.AddState(RootHFSM, _rootFSM);
		_stateFactory.AddState(GroundHFSM, new PlayerGroundHFSM(this, GroundHFSM, _rootFSM, _stateFactory));

		_stateFactory.AddState(Idle, new PlayerIdle(this, Idle, _rootFSM, _stateFactory));
		_stateFactory.AddState(PlayerStateEnum.Jump, new PlayerJump(this, PlayerStateEnum.Jump, _rootFSM, _stateFactory));
		_stateFactory.AddState(Run, new PlayerRun(this, Run, _rootFSM, _stateFactory));


		InitStateCondition();
	}

	void InitDetects() {
		_groundDetect = new BaseRayDetect(
			ConfigData.GroundL, ConfigData.GroundR, _transform, ConfigData.DetectDistance,
			LayerStrings.Ground,
			(hitL, hitR) => hitL || hitR);

		_ceilingDetect = new BaseRayDetect(
			ConfigData.CeilinL, ConfigData.CeilinR, _transform, ConfigData.DetectDistance,
			LayerStrings.Ground,
			(hitL, hitR) => hitL || hitR);

		_wallDetect = new BaseRayDetect(
			ConfigData.WallUp, ConfigData.WallDown, _transform, ConfigData.DetectDistance,
			LayerStrings.Ground,
			(hitUp, hitDown) => hitUp && hitDown);

		_edgeDetect = new BaseRayDetect(
			ConfigData.WallUp, ConfigData.WallDown, _transform, ConfigData.DetectDistance,
			LayerStrings.Ground,
			(hitUp, hitDown) => !hitUp && hitDown);
	}
#warning Todo!
	void InitStateCondition() {
		//Todo
	}

	#region Funcs of CollsionDetect
	public bool IsOnGround { get => _groundDetect.IsDetect(Vector2.down); }
	public bool IsTouchCeiling { get => _ceilingDetect.IsDetect(Vector2.up); }
	public bool IsFacingWall { get => _wallDetect.IsDetect(new Vector2(transform.localScale.x, 0)); }
	public bool IsTouchingEdge { get => _edgeDetect.IsDetect(new Vector2(transform.localScale.x, 0)); }

	#endregion

	#region Funcs of InputMgr
	public float XInput { get => _inputMgr.XInput; set { } }
	public bool Jump { get => _inputMgr.Jump; set { } }
	public bool DownInput { get => _inputMgr.DownInput; set { } }
	#endregion

	#region Funcs Of Other AnimMgr
	public void PlayAnim(PlayerStateEnum state) => _animeMgr.PlayAnim(state);

	public void PauseAnim() => _animeMgr.PauseAnim();

	public void ResumeAnim() => _animeMgr.ResumeAnim();

	public void SetAnimPlaySpeed(float speed) => _animeMgr.SetAnimPlaySpeed(speed);

	public bool IsAnimPlayOver(int animLayerIndex = 0) => _animeMgr.IsAnimPlayOver(animLayerIndex);

	public void AddAnim(PlayerStateEnum state, string animName) => _animeMgr.AddAnim(state, animName);


	#endregion

	#region RidigBodyMgr
	public void SetVelocity(Vector2 v) => _rigidBodyMgr.SetVelocity(v);

	public void SetGravity(float gravity) => _rigidBodyMgr.SetGravity(gravity);

	public void ResetGravity() => _rigidBodyMgr.ResetGravity();

	#endregion

}
